package ran.quzitech.rnble.utils;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * Created by ran on 2016/11/24.
 */
public class ScanRecordUtil {

    public static ParsedAd parseData(byte[] adv_data) {
        ParsedAd parsedAd = new ParsedAd();
        ByteBuffer buffer = ByteBuffer.wrap(adv_data).order(ByteOrder.LITTLE_ENDIAN);
        while (buffer.remaining() > 2) {
            byte length = buffer.get();
            if (length == 0)
                break;
            byte type = buffer.get();
            length -= 1;
            switch (type) {
                case 0x01: // Flags
                    parsedAd.setFlags(buffer.get());
                    length--;
                    break;
                case 0x17:
                    byte sb[] = new byte[length];

                    buffer.get(sb, 0, length);
                    length = 0;
                   // Log.v("SwitchOnActivity", "0x17:" + );
                    parsedAd.setMacAddress(DataUtil.byteArrayToHex(sb).replaceAll(" ",""));

                    break;

                case 0x02: // Partial list of 16-bit UUIDs
                case 0x03: // Complete list of 16-bit UUIDs
                case 0x14: // List of 16-bit Service Solicitation UUIDs
                    while (length >= 2) {
                        parsedAd.getUuids().add(UUID.fromString(String.format(
                                "%08x-0000-1000-8000-00805f9b34fb", buffer.getShort())));
                        length -= 2;
                    }
                    break;

                case 0x04: // Partial list of 32 bit service UUIDs
                case 0x05: // Complete list of 32 bit service UUIDs
                    while (length >= 4) {
                        parsedAd.getUuids().add(UUID.fromString(String.format(
                                "%08x-0000-1000-8000-00805f9b34fb", buffer.getInt())));
                        length -= 4;
                    }
                    break;
                case 0x06: // Partial list of 128-bit UUIDs
                case 0x07: // Complete list of 128-bit UUIDs
                case 0x15: // List of 128-bit Service Solicitation UUIDs
                    while (length >= 16) {
                        long lsb = buffer.getLong();
                        long msb = buffer.getLong();
                        parsedAd.getUuids().add(new UUID(msb, lsb));
                        length -= 16;
                    }
                    break;
                case 0x08: // Short local device name
                case 0x09: // Complete local device name
                    byte sb09[] = new byte[length];
                    buffer.get(sb09, 0, length);
                    length = 0;
                    parsedAd.setLocalName(new String(sb09).trim());
                    break;
                case (byte) 0xFF: // Manufacturer Specific Data
//                    parsedAd.manufacturer = buffer.getShort();
//                    length -= 2;
                    byte sbManufacturer[] = new byte[length];
                    buffer.get(sbManufacturer, 0, length);
                    length = 0;
                    // Log.v("SwitchOnActivity", "0x17:" + );
                    parsedAd.setManufacturer(DataUtil.byteArrayToHex(sbManufacturer).replaceAll(" ",""));

                    break;
                default: // skip
                    break;

            }
            if (length > 0) {
                buffer.position(buffer.position() + length);
            }
        }
        return parsedAd;
    }


    /**
     * Parse the UUIDs from an advert for filtering purposes. This code is based on code from
     * https://code.google.com/p/android/issues/detail?id=59490 and is required to work around issues in the Android API
     * with UUID filtering on scan results.
     *
     * @param advData Advertising data from a scan result.
     * @return List of UUIDs found in the advertising data.
     */
    public static List<UUID> uuidsFromAdvert(final byte[] advData) {
        final List<UUID> uuids = new ArrayList<UUID>();

        // Pointer within advData to the start of the current ad element being processed.
        int ptrToAdElement = 0;

        // Offsets from start of ad element (i.e. from ptrToAdElement).
        final int OFFSET_LENGTH = 0;
        final int OFFSET_TYPE = 1;
        final int OFFSET_DATA = 2;

        final byte AD_TYPE_UUID_16BIT = 0x02;
        final byte AD_TYPE_UUID_16BIT_LIST = 0x03;
        final byte AD_TYPE_UUID_128BIT = 0x06;
        final byte AD_TYPE_UUID_128BIT_LIST = 0x07;

        final int UUID_16_LENGTH = 2;
        final int UUID_128_LENGTH = 16;

        final String BASE_UUID_FORMAT = "%08x-0000-1000-8000-00805f9b34fb";

        while (ptrToAdElement < advData.length - 1) {
            final byte length = advData[ptrToAdElement + OFFSET_LENGTH];

            // The advert data returned by the Android API is padded out with trailing zeroes, so if we reach a
            // zero length then we are done.
            if (length == 0)
                break;

            // Check that there is enough remaining data in the advert for the indicated length.
            if (length > (advData.length - ptrToAdElement - 1)) {
                // This was a malformed advert so return an empty list, even if we got some UUIDs already.
                uuids.clear();
                return uuids;
            }

            final byte adType = advData[ptrToAdElement + OFFSET_TYPE];

            switch (adType) {
                case AD_TYPE_UUID_16BIT:
                case AD_TYPE_UUID_16BIT_LIST:
                    for (int i = length; i > UUID_16_LENGTH - 1; i -= UUID_16_LENGTH) {
                        int uuid16 = (advData[ptrToAdElement + OFFSET_DATA] & 0xFF);
                        uuid16 |= ((advData[ptrToAdElement + OFFSET_DATA + 1] & 0xFF) << 8);
                        uuids.add(UUID.fromString(String.format(BASE_UUID_FORMAT, uuid16)));
                    }
                    break;
                case AD_TYPE_UUID_128BIT:
                case AD_TYPE_UUID_128BIT_LIST:
                    for (int i = length; i > UUID_128_LENGTH - 1; i -= UUID_128_LENGTH) {
                        long msb = ((advData[ptrToAdElement + OFFSET_DATA] & 0xFF) << 56);
                        msb |= ((advData[ptrToAdElement + OFFSET_DATA + 1] & 0xFF) << 48);
                        msb |= ((advData[ptrToAdElement + OFFSET_DATA + 2] & 0xFF) << 40);
                        msb |= ((advData[ptrToAdElement + OFFSET_DATA + 3] & 0xFF) << 32);
                        msb |= ((advData[ptrToAdElement + OFFSET_DATA + 4] & 0xFF) << 24);
                        msb |= ((advData[ptrToAdElement + OFFSET_DATA + 5] & 0xFF) << 16);
                        msb |= ((advData[ptrToAdElement + OFFSET_DATA + 6] & 0xFF) << 8);
                        msb |= ((advData[ptrToAdElement + OFFSET_DATA + 7] & 0xFF) << 0);
                        long lsb = ((advData[ptrToAdElement + OFFSET_DATA + 8] & 0xFF) << 56);
                        lsb |= ((advData[ptrToAdElement + OFFSET_DATA + 9] & 0xFF) << 48);
                        lsb |= ((advData[ptrToAdElement + OFFSET_DATA + 10] & 0xFF) << 40);
                        lsb |= ((advData[ptrToAdElement + OFFSET_DATA + 11] & 0xFF) << 32);
                        lsb |= ((advData[ptrToAdElement + OFFSET_DATA + 12] & 0xFF) << 24);
                        lsb |= ((advData[ptrToAdElement + OFFSET_DATA + 13] & 0xFF) << 16);
                        lsb |= ((advData[ptrToAdElement + OFFSET_DATA + 14] & 0xFF) << 8);
                        lsb |= ((advData[ptrToAdElement + OFFSET_DATA + 15] & 0xFF) << 0);
                        uuids.add(new UUID(msb, lsb));
                    }
                    break;
                default:
                    // An advert type we don't care about.
                    break;
            }

            // Length byte isn't included in length, hence the +1.
            ptrToAdElement += length + 1;
        }

        return uuids;
    }

}
